import { toggle } from "./checkbox";

export let dropDownController = ['$scope', function($scope) {
    $scope.options = [];

    this.add_option = function(title, value) {
        $scope.options.push({
            'title': title,
            'value': value
        });
        if(value === $scope.model) {
            this.update_title(value)
        };
    };

    this.remove_option = function(title, value) {
        for(var index in $scope.options)
            if($scope.options[index].value === value &&
                $scope.options[index].title === title) {

                $scope.options.splice(index, 1);
                // Remove only one item
                break;
            };
    };

    this.update_model = function(title, value) {
        if($scope.model !== value) {
            $scope.model = value;
        }
    };

    this.update_title = function(value) {
        var changed = false;

        for(var index in $scope.options)
            if($scope.options[index].value === value) {
                $scope.title = $scope.options[index].title;
                changed = true;
            }

        if(changed) {
            $scope.text_class = 'text';
        } else {
            $scope.title = $scope.original_title;
            $scope.text_class = 'default text';
        }
    };

    this.getCurrentIndex = function() {
        for (let i = 0; i < $scope.options.length; i++) {
            if ($scope.options[i].value === $scope.model) {
                return i
            }
        }
        return -1
    }

    this.selectOptionIndex = function(index: number) {
        let option = $scope.options[index]
        this.update_model(option.title, option.value)
        this.update_title(option.value)

        $scope.$apply()
    }

    this.next = function() {
        let index = this.getCurrentIndex()
        let nextIndex = Math.min(index + 1, $scope.options.length - 1)
        this.selectOptionIndex(nextIndex)
        $scope.$apply()
    }

    this.previous = function() {
        let index = this.getCurrentIndex()
        let prevIndex = Math.max(index - 1, 0)
        this.selectOptionIndex(prevIndex)
        $scope.$apply()
    }

    this.active = function(value) {
        return $scope.model === value;
    }

}]

export let dropdown = function() {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        controller: 'DropDownController',
        scope: {
            title: '@',
            open: '@',
            model: '=ngModel',
            change: '&?ngChange'
        },
        template: '<div class="{{ dropdown_class }}" tabindex="0">' +
            '<div class="{{text_class}}">{{ title }}</div>' +
            '<i class="dropdown icon"></i>' +
            '<div class="{{ menu_class }}"  ng-transclude>' +
            '</div>' +
            '</div>',
        link: function(scope, element, attrs, DropDownController: any) {
            let isMouseDown = false
            scope.dropdown_class = 'ui selection dropdown';
            scope.menu_class = 'menu transition hidden';
            scope.text_class = 'default text';
            scope.original_title = scope.title;

            if(scope.open === 'true') {
                scope.is_open = true;
                scope.dropdown_class = scope.dropdown_class + ' active visible';
                scope.menu_class = scope.menu_class + ' visible';
            } else {
                scope.is_open = false;
            }

            /*
             * Watch for ng-model changing
             */
            scope.element = element;
            scope.$watch('model', function(value) {
                // update title or reset the original title if its empty
                DropDownController.update_title(value);
                if (scope.change) scope.change()
            });

            /**
             * Close the dropdown
             */
            function close() {
                scope.is_open = false
                scope.$apply(function() {
                    scope.dropdown_class = 'ui selection dropdown';
                    scope.menu_class = 'menu transition hidden';
                });
            }

            /**
             * Open the dropdown
             */
            function open() {
                scope.is_open = true
                scope.$apply(function() {
                    scope.dropdown_class = 'ui selection dropdown active visible';
                    scope.menu_class = 'menu transition visible';
                });
            }

            /**
             * When element is focussed (e.g. using TAB) open the dropdown automatically.
             * If isMouseDown is true, then the user clicked the dropdown menu, in which case
             * we do nothing and let the click event handle the action
             * @param event MouseEvent generated by addEventListener
             */
            function onFocus(event: FocusEvent) {
                if (!scope.is_open && !isMouseDown) {
                    open()
                }
            }

            /**
             * When dropdown is clicked, toggle the open/close state of the dropdown
             */
            function onDropdownClicked() {
                toggle()
            }

            function toggle() {
                if (scope.is_open) {
                    close()
                } else {
                    open()
                }
            }

            function onMouseDown(event: MouseEvent) {
                isMouseDown = true
            }

            function onMouseUp(event: MouseEvent) {
                isMouseDown = false
            }

            /**
             * When element outside dropdown (i.e. body) is clicked, close the dropdown if it is already open
             * @param event MouseEvent generated by addEventListener
             */
            function onWindowBodyClicked(event: MouseEvent) {
                if (!element[0].contains(event.target)) {
                    if (scope.is_open) {
                        close()
                    }
                }
            }

            /**
             * When escape key is pressed, close dropdown if open
             * @param event MouseEvent generated by addEventListener
             */
            function onEscKeyPressed(event: KeyboardEvent) {
                if (scope.is_open) {
                    close()
                    element.blur()
                }
            }

            /**
             * When any keyup event is emitted from document
             * @param event Keyboard event generated from addEventListener
             */
            function onWindowKeyUp(event: KeyboardEvent) {
                if (event.key === "Escape") {
                    onEscKeyPressed(event)
                }
                if (event.key === "Enter") {
                    if (scope.is_open) {
                        close()
                    }
                }
            }

            /**
             * When the dropdown menu is open, handle arrow keys (up and down) for nativating elements
             * @param event KeyboardEvent generated from addEventListener
             */
            function onWindowKeyDown(event: KeyboardEvent) {
                if (!scope.is_open) {
                    return
                }
                if(["Space","ArrowUp","ArrowDown","ArrowLeft","ArrowRight"].indexOf(event.code) > -1) {
                    event.preventDefault();
                }
                switch (event.code) {
                    case "ArrowUp":
                        DropDownController.previous()
                        break
                    case "ArrowDown":
                        DropDownController.next()
                        break
                    case "Space":
                        toggle()
                        break
                }
            }

            /**
             * When element is blurred (opposite of focus) close the dropdown menu
             * @param event FocusEvent generated from addEventListener
             */
            function onBlur(event: FocusEvent) {
                if (scope.is_open) {
                    close()
                }
            }

            /**
             * Set up event handlers for key/mouse interaction
             */
            element.on('click', onDropdownClicked)
            element.on('focus', onFocus)
            element.on('mousedown', onMouseDown)
            element.on('blur', onBlur)
            document.body.addEventListener('click', onWindowBodyClicked)
            window.addEventListener('keyup', onWindowKeyUp)
            window.addEventListener('keydown', onWindowKeyDown)
            window.addEventListener('mouseup', onMouseUp)

            /**
             * Tear down event handlers when component is removed
             */
            scope.$on('$destroy', function() {
                document.body.removeEventListener('click', onWindowBodyClicked)
                window.removeEventListener('keyup', onWindowKeyUp)
                window.removeEventListener('keydown', onWindowKeyDown)
                window.removeEventListener('mouseup', onMouseUp)
            });
        }
    };
}

export let dropdownGroup = function() {
    return {
        restrict: 'AE',
        replace: true,
        transclude: true,
        require: '^dropdown',
        scope: {
            title: '=title',
            value: '=value'
        },
        template: '<div class="item" ng-class="active()" ng-transclude>{{ item_title }}</div>',
        link: function(scope, element, attrs, DropDownController: any) {

            // Check if title= was set... if not take the contents of the dropdown-group tag
            // title= is for dynamic variables from something like ng-repeat {{variable}}
            if(scope.title === undefined) {
                scope.item_title = attrs.title || element[0]?.innerHTML || "";
            } else {
                scope.item_title = scope.title;
            }
            if(scope.value === undefined) {
                scope.item_value = attrs.value || scope.item_title;
            } else {
                scope.item_value = scope.value;
            }

            scope.active = function() {
                if(DropDownController.active(scope.item_value)) {
                    return "selected active";
                }
            }

            // Keep this option
            DropDownController.add_option(scope.item_title, scope.item_value);

            //
            // Menu item click handler
            //
            element.bind('click', function() {
                DropDownController.update_model(scope.item_title, scope.item_value);
            });

            scope.$on('$destroy', function() {
                DropDownController.remove_option(scope.item_title, scope.item_value);
            });

        }
    };
};